use core::cmp::min;
use core::ptr;
use hierr::{set_errno, Error};

#[allow(dead_code)]
pub fn env_string<'a>(name: &str, value: &'a mut [u8]) -> &'a [u8] {
    let mut buf = [0_u8; 128];
    let name = env_name(name, &mut buf);
    let val = unsafe { libc::getenv(name) };
    if val.is_null() {
        return &value[0..0];
    }
    let len = min(value.len(), unsafe { libc::strlen(val) });
    unsafe {
        ptr::copy(val.cast::<u8>(), value.as_mut_ptr(), len);
    }
    &value[0..len]
}

#[allow(dead_code)]
pub fn env_signed<T: TryFrom<i64>>(name: &str, base: i32, def: T) -> T
where
    T: TryFrom<i64> + Default,
{
    env_number::<T, _, _>(name, base, |n, e, b| unsafe { libc::strtoll(n, e, b) }).unwrap_or(def)
}

#[allow(dead_code)]
pub fn env_unsigned<T: TryFrom<u64>>(name: &str, base: i32, def: T) -> T
where
    T: TryFrom<i64> + Default,
{
    env_number::<T, _, _>(name, base, |n, e, b| unsafe { libc::strtoull(n, e, b) }).unwrap_or(def)
}

fn env_number<T, N, F>(name: &str, base: i32, f: F) -> Result<T, Error>
where
    T: TryFrom<N> + Default,
    F: Fn(*const i8, *mut *mut i8, i32) -> N,
{
    let mut buf = [0_u8; 128];
    let name = env_name(name, &mut buf);
    let val = unsafe { libc::getenv(name) };
    if val.is_null() {
        return Ok(T::default());
    }
    set_errno(0);

    let val = f(val, ptr::null_mut(), base);
    let err = Error::last_error();
    if err.errno == 0 {
        T::try_from(val).map_err(|_| Error::new(libc::EINVAL))
    } else {
        Err(err)
    }
}

fn env_name(name: &str, buf: &mut [u8; 128]) -> *const i8 {
    if !name.is_empty() {
        if name.as_bytes()[name.len() - 1] == 0 {
            return name.as_bytes().as_ptr().cast::<i8>();
        }
        let len = min(127, name.len());
        unsafe {
            ptr::copy_nonoverlapping(name.as_bytes().as_ptr(), buf.as_mut_ptr(), len);
        }
        buf[len] = 0;
    } else {
        buf[0] = 0;
    }
    buf.as_ptr().cast::<i8>()
}
